package com.lagou.edu.factory;

import com.lagou.edu.annotation.*;
import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author 应癫
 * <p>
 * 工厂类，生产对象（使用反射技术）
 */
public class AnnotatedBeanFactory {

    /**
     * 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
     * 任务二：对外提供获取实例对象的接口（根据id获取）
     */

    private static Map<String, Object> map = new HashMap<>();  // 存储对象


    static {
        String scanPackage = "com.lagou";
        try {
            Reflections reflections = new Reflections(scanPackage);

            initializeBeans(reflections, Component.class);
            initializeBeans(reflections, Service.class);
            initializeBeans(reflections, Repository.class);

            populateBeans();

            postBeanIntialized();


        } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    private static void postBeanIntialized() {
        ProxyFactory proxyFactory = (ProxyFactory) getBean("proxyFactory");
        for (Map.Entry<String, Object> bean : map.entrySet()) {
            final Object beanObj = bean.getValue();
            final Class<?> beanClass = beanObj.getClass();
            if (beanClass.isAnnotationPresent(Transactional.class)) {
                Object proxyBean = proxyFactory.getCglibProxy(beanObj);
                map.put(bean.getKey(), proxyBean);
            }
        }
    }

    private static void populateBeans() throws IllegalAccessException, InvocationTargetException {
        for (Map.Entry<String, Object> bean : map.entrySet()) {
            final Object beanObj = bean.getValue();
            final Class<?> beanClass = beanObj.getClass();
            final Method[] methods = beanClass.getMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(Autowired.class)) {
                    Autowired autowired = method.getAnnotation(Autowired.class);
                    Object refBean;
                    if (StringUtils.isNotEmpty(autowired.value())) {
                        refBean = getBean(autowired.value());
                    } else {
                        Class refType = method.getParameterTypes()[0];
                        refBean = getBeanByType(refType);
                    }
                    method.invoke(beanObj, refBean);
                }
            }
            final Field[] fields = beanClass.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    Object refBean;
                    if (StringUtils.isNotEmpty(autowired.value())) {
                        refBean = getBean(autowired.value());
                    } else {
                        Class refType = field.getType();
                        refBean = getBeanByType(refType);
                    }
                    field.setAccessible(true);
                    field.set(beanObj, refBean);
                }
            }
            map.put(bean.getKey(), beanObj);
        }
    }

    private static void initializeBeans(Reflections reflections, Class annotationClass) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        final Set<Class<?>> beanClasses = reflections.getTypesAnnotatedWith(annotationClass);
        for (Class<?> beanClass : beanClasses) {
            Annotation component = beanClass.getAnnotation(annotationClass);
            // 通过反射技术实例化对象
            Object o = beanClass.newInstance();  // 实例化之后的对象
            // 存储到map中待用
            String id = component.annotationType().getMethod("value").invoke(component).toString();
            if (StringUtils.isEmpty(id)) {
                id = StringUtils.uncapitalize(beanClass.getSimpleName());
            }
            map.put(id, o);
        }
    }

    private static Object getBeanByType(Class beanClass) {
        for (Object value : map.values()) {
            if (value.getClass().getName().equals(beanClass.getName())
                    || beanClass.isInstance(value)) {
                return value;
            }
        }
        return null;
    }


    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static Object getBean(String id) {
        return map.get(id);
    }

}
